package com.zz.config.shiro;

import com.zz.filter.CustomPermissionsAuthorizationFilter;
import com.zz.filter.MyFormAuthenticationFilter;
import com.zz.filter.StatelessAuthcFilter;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.filters.CorsFilter;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author HUAWEI
 */
@Slf4j
@Configuration
public class ShiroConfig {

//    @Autowired
//    private MyRealm myRealm;


    /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * Filter Chain定义说明
     * 1、一个URL可以配置多个Filter，使用逗号分隔
     * 2、当设置多个过滤器时，全部验证通过，才视为通过
     * 3、部分过滤器可指定参数，如perms，roles
     *
     * anon（匿名）  org.apache.shiro.web.filter.authc.AnonymousFilter
     * authc（身份验证）       org.apache.shiro.web.filter.authc.FormAuthenticationFilter
     * authcBasic（http基本验证）    org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
     * logout（退出）        org.apache.shiro.web.filter.authc.LogoutFilter
     * noSessionCreation（不创建session） org.apache.shiro.web.filter.session.NoSessionCreationFilter
     * perms(许可验证)  org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
     * port（端口验证）   org.apache.shiro.web.filter.authz.PortFilter
     * rest  (rest方面)  org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
     * roles（权限验证）  org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
     * ssl （ssl方面）   org.apache.shiro.web.filter.authz.SslFilter
     * member （用户方面）  org.apache.shiro.web.filter.authc.UserFilter
     * user  表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe
     */
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager manager){
        if(log.isDebugEnabled()){
            log.debug("ShiroConfiguration.shirFilter()");
        }
        // 1. 创建过滤器工厂
        ShiroFilterFactoryBean bean  = new ShiroFilterFactoryBean();
        // 2. 必须设置 SecurityManager: 设置安全管理器
        bean.setSecurityManager(manager);
        //增加自定义过滤
        Map<String, Filter> filters = new LinkedHashMap<>(5);
        filters.put("user",new StatelessAuthcFilter());
        filters.put("authc",new CorsFilter());

        //filters.put("authc", new UserFormAuthenticationFilter());
        filters.put("perms", new CustomPermissionsAuthorizationFilter());
        filters.put("authc",new MyFormAuthenticationFilter());

        bean.setFilters(filters);
        //拦截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();

        bean.setLoginUrl("/user/login");
        //<!-- 过滤链定义，从上向下顺序执行，一般将 /**放在最为下边
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/user/register", "anon");

        //配置退出过滤器
        filterChainDefinitionMap.put("/user/logout", "logout");
        // 使用该过滤器过滤所有的链接
        //filterChainDefinitionMap.putAll(ShiroConfigConstant.filterMap);

        //配置记住我或认证通过可以访问的地址
        //添加存在用户的过滤器（rememberMe）
        filterChainDefinitionMap.put("/**", "user");
        filterChainDefinitionMap.put("/**", "authc");

        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return bean;
    }

    //配置 SecurityManager
    @Bean(value = "securityManager")
    public DefaultWebSecurityManager securityManager(@Qualifier("myRealm") MyRealm myRealm) {
        //1 创建defaultWebSecurityManager 对象
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        /*
        //2 创建加密对象，并设置相关属性
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        //2.1 采用md5 加密
        matcher.setHashAlgorithmName("md5");
        //2.2 迭代加密次数
        matcher.setHashIterations(3);
        //3 将加密对象存储到myRealm中
        myRealm.setCredentialsMatcher(matcher);
         */

        //4.5 设置rememberMe
        //defaultWebSecurityManager.setRememberMeManager(rememberMeManager());

        // 自定义的shiro session 缓存管理器
        defaultWebSecurityManager.setSessionManager(sessionManager());

        //4 将myRealm 存入 defaultWebSecurityManager 对象,推荐放到最后，不然某些情况下不生效
        defaultWebSecurityManager.setRealm(myRealm);

        //6 返回
        return defaultWebSecurityManager;
    }

    @Bean
    public MyRealm myRealm(){
        return new MyRealm();
    }

    /**
     * 开启shiro 的AOP注解支持
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 自定义的 shiro session 缓存管理器，用于跨域等情况下使用 token 进行验证，不依赖于sessionId
     * @return
     */
    @Bean
    public SessionManager sessionManager(){
        //将我们继承后重写的shiro session 注册
        ShiroSessionManager shiroSessionManager = new ShiroSessionManager();
        //如果后续考虑多tomcat部署应用，可以使用shiro-redis开源插件来做session 的控制，或者nginx 的负载均衡

        shiroSessionManager.setSessionDAO(new EnterpriseCacheSessionDAO());
        return shiroSessionManager;
    }

    //cookie 属性设置
    @Bean
    public SimpleCookie rememberMeCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        //只能通过http访问cookie，js不能
        cookie.setPath("/");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(30 * 24 * 60 * 60);
        return cookie;
    }

    //创建Shiro的cookie管理对象
    /**
     * cookie管理对象;
     * rememberMeManager()方法是生成rememberMe管理器，而且要将这个rememberMe管理器设置到securityManager中
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
        return cookieRememberMeManager;
    }

}
